home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
DJGPP
/
CBGRX103.ZIP
/
contrib
/
libgrx
/
src
/
scanpoly.c
< prev
next >
Wrap
Text File
|
1993-12-06
|
7KB
|
229 lines
/**
** SCANPOLY.C
**
** Copyright (C) 1992, Csaba Biegl
** 820 Stirrup Dr, Nashville, TN, 37221
** csaba@vuse.vanderbilt.edu
**
** This file is distributed under the terms listed in the document
** "copying.cb", available from the author at the address above.
** A copy of "copying.cb" should accompany this file; if not, a copy
** should be available from where this file was obtained. This file
** may not be distributed without a verbatim copy of "copying.cb".
** You should also have received a copy of the GNU General Public
** License along with this program (it is in the file "copying");
** if not, write to the Free Software Foundation, Inc., 675 Mass Ave,
** Cambridge, MA 02139, USA.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**/
#include "grx.h"
#include "libgrx.h"
#include "clipping.h"
#include "scale.h"
#include "gmalloc.h"
typedef enum {
inactive,
active,
passed
} edgestatus;
typedef struct _scansegment_ {
struct _scansegment_ *next;
int X1,X2;
} scansegment;
typedef struct {
scansegment seg;
edgestatus status;
int x1,x2,y1,y2;
int slope,error;
int dx,dy;
} polyedge;
void _GrScanPolygon(int n,int pt[][2],
int is_XOR_color,
_GrPixelDrawProc pixelproc,
_GrLineDrawProc borderproc,
_GrScanLineProc scanfillproc,
void *fillarg)
{
polyedge *edgetable,*ep;
scansegment *firstpt,*firstseg,*pp,*sp,*prev;
int prevx,prevy;
int minx,maxx,miny,maxy;
int realedges;
int X1,X2,Y;
MOUSE_FLAG;
#define add_scanpoint(seg,x) do { \
X1 = (x); \
prev = NULL; \
for(pp = firstpt; pp != NULL; prev = pp,pp = pp->next) \
if(pp->X1 >= X1) break; \
(seg)->next = pp; \
(seg)->X1 = X1; \
*(prev ? &prev->next : &firstpt) = (seg); \
} while(0)
#define add_scansegment(seg,x1,x2) do { \
char overlap = FALSE; \
X1 = (x1); \
X2 = (x2); \
prev = NULL; \
for(sp = firstseg; sp != NULL; prev = sp,sp = sp->next) { \
if((sp->X1 <= X2) && (X1 <= sp->X2)) { \
overlap = TRUE; \
if(X1 < sp->X1) sp->X1 = X1; \
if(X2 > sp->X2) { \
prev = sp; \
while((sp = sp->next) != NULL) { \
if(sp->X1 > X2) break; \
if(sp->X2 > X2) X2 = sp->X2; \
} \
prev->X2 = X2; \
prev->next = sp; \
} \
break; \
} \
if(sp->X1 > X2) break; \
} \
if(!overlap) { \
(seg)->next = sp; \
(seg)->X1 = X1; \
(seg)->X2 = X2; \
*(prev ? &prev->next : &firstseg) = (seg); \
} \
} while(0)
if((n > 1) && (pt[0][0] == pt[n-1][0]) && (pt[0][1] == pt[n-1][1])) n--;
if(n <= 2) {
if(n <= 0) return;
minx = pt[0][0];
miny = pt[0][1];
if(n == 1) {
MOUSE_BLOCK(CURC,minx,miny,minx,miny);
(*pixelproc)(minx,miny,fillarg);
}
else {
maxx = pt[1][0];
maxy = pt[1][0];
MOUSE_BLOCK(CURC,minx,miny,maxx,maxy);
(*borderproc)(minx,miny,maxx,maxy,fillarg);
}
MOUSE_UNBLOCK();
return;
}
edgetable = (polyedge *)_GrGetTempBuffer(sizeof(polyedge) * (n + 2));
if(edgetable == NULL) return;
/*
* Build the edge table. Store only those edges which are in the valid
* Y region. Clip them in Y if ncessary. Store them with the endpoints
* ordered by Y in the edge table.
*/
prevx = pt[0][0];
prevy = pt[0][1];
minx = miny = 32000;
maxx = maxy = (-32000);
realedges = 0;
ep = edgetable;
while(--n >= 0) {
if(pt[n][1] >= prevy) {
ep->x1 = prevx;
ep->y1 = prevy;
ep->x2 = prevx = pt[n][0];
ep->y2 = prevy = pt[n][1];
}
else {
ep->x2 = prevx;
ep->y2 = prevy;
ep->x1 = prevx = pt[n][0];
ep->y1 = prevy = pt[n][1];
}
if(ep->y1 > _GrHiY) continue;
if(ep->y2 < _GrLoY) continue;
if(ep->y1 < _GrLoY) {
SCALE(X1,(ep->x1 - ep->x2),(ep->y2 - _GrLoY),(ep->y2 - ep->y1));
ep->x1 = ep->x2 + X1;
ep->y1 = _GrLoY;
}
if(ep->y2 > _GrHiY) {
SCALE(X2,(ep->x2 - ep->x1),(_GrHiY - ep->y1),(ep->y2 - ep->y1));
ep->x2 = ep->x1 + X2;
ep->y2 = _GrHiY;
}
if(ep->y1 < miny) miny = ep->y1;
if(ep->y2 > maxy) maxy = ep->y2;
if(ep->x2 > ep->x1) {
if(ep->x1 < minx) minx = ep->x1;
if(ep->x2 > maxx) maxx = ep->x2;
}
else {
if(ep->x1 > maxx) maxx = ep->x1;
if(ep->x2 < minx) minx = ep->x2;
}
ep->status = inactive;
realedges++;
ep++;
}
if(realedges == 0) return;
if(minx > _GrHiX) return;
if(maxx < _GrLoX) return;
MOUSE_BLOCK(CURC,minx,miny,maxx,maxy);
/*
* Scan for every row between ymin and ymax.
* Build a linked list of disjoint segments to fill. Rules:
* (1) a horizontal edge in the row contributes a segment
* (2) any other edge crossing the row contributes a point
* (3) every segment between even and odd points is filled
*/
for(Y = miny; Y <= maxy; Y++) {
firstpt = NULL;
firstseg = NULL;
for(n = realedges,ep = edgetable; --n >= 0; ep++) {
switch(ep->status) {
case inactive:
if(ep->y1 != Y) break;
ep->dx = ep->x2 - ep->x1;
ep->dy = ep->y2 - ep->y1;
if(ep->dy == 0) {
add_scansegment(&ep->seg,
((ep->dx > 0) ? ep->x1 : ep->x2),
((ep->dx > 0) ? ep->x2 : ep->x1)
);
ep->status = passed;
break;
}
ep->slope = ep->dx / ep->dy;
if(ep->slope && !is_XOR_color)
(*borderproc)(ep->x1,ep->y1,ep->x2,ep->y2,fillarg);
if((ep->dx %= ep->dy) < 0) { ep->slope--; ep->dx += ep->dy; }
ep->error = ep->dy >> 1;
ep->status = active;
case active:
if((ep->y2 == Y) && (Y != maxy)) { ep->status = passed; break; }
add_scanpoint(&ep->seg,ep->x1);
if((ep->error -= ep->dx) < 0) { ep->x1++; ep->error += ep->dy; }
ep->x1 += ep->slope;
break;
default:
break;
}
}
while((pp = firstpt) != NULL) {
firstpt = pp->next->next;
add_scansegment(pp,pp->X1,pp->next->X1);
}
for(sp = firstseg; sp != NULL; sp = sp->next) {
(*scanfillproc)(sp->X1,sp->X2,Y,fillarg);
}
}
MOUSE_UNBLOCK();
}